import com.sun.org.apache.regexp.internal.RE
import javafx.beans.binding.IntegerBinding
import java.io.BufferedReader
import java.lang.Exception
import java.lang.NumberFormatException
import java.lang.StringBuilder
import java.util.*

/**
 * main方法
 * fun function(方法,函数) 在kotlin中,用fun标记的都是一个方法,只要是方法,就必须拿fun来标记
 * main 方法名 其中main为关键字,如果是其他方法,随意命名
 * args 参数名,对应Java中args
 * : Array<String> 冒号在kotlin中非常重要,代表继承,实现,或者类型 Array<String>是一个数组
 * 在kotlin中不区分数组和集合的概念,统称为集合也可以,没有[]中括号的概念
 * println 是对Java中System.out.println的封装
 * kotlin中每句话后面是没有分号的
 */


/**
 * 方法(函数) 有返回值,没返回值的方法,在kotlin中其实所有的方法都有返回值,只是没有返回值的方法他的
 * 返回值可以省略 Unit 等同于Java中void标记
 * a: Int a是参数名,:Int是类型
 * 整个方法后的: Int 代表当前方法的返回值
 */
//fun max(a: Int, b: Int): Int {
//    return if (a > b) a else b
//}

/**
 * kotlin特性,表达式方法体(函数体)
 * 如果一个方法函数体是由单个表达式构成,可以用这个表达式作为完整的函数体,并且去掉大括号和return语句
 * 返回:Int也是可以省略的
 */
fun max(a: Int, b: Int) = if (a > b) a else b

/**
 * 变量
 *  val (value) 不可变引用,在初始化之后不能再次赋值,对应Java中final修饰符
 *  var (variable) 可变引用,这种变量的值可以被改变,对应Java中的普通变量
 *  :String可以省略,也就是val和var可以自动识别类型,初始化是什么类型,之后使用就是什么类型
 *  初始化是一个int,再次赋值时也必须是一个int,不能改变类型,可以改变值
 *  初始化直接写12L代表一个Long,写12f代表一个double,写true代表值是一个boolean
 *  有个特殊情况,使用val声明list的时候,他的集合中的值是可以改变的
 *
 */
val str: String = "这是一个String"
var a = 0
//val list = arrayListOf("Java")//arrayListOf 相当于new一个list

/**
 * 字符串模板
 * 使用$符号可以在字符串中引用某些变量,甚至可以加{}大括号来进行计算和判断
 * 注意在写句式的时候,先写出完整字符串,再加入${} 这样不会乱
 */
val name = "LC"

/**
 * 枚举
 * 在kotlin中唯一一个能用到分号的方法就是在枚举中,代表换行是一个方法了,而不是其中一个枚举
 */
enum class Color(val r: Int, val g: Int, val b: Int) {
    RED(255, 0, 0),
    ORANGE(255, 165, 0),
    YELLOW(255, 255, 0),
    GREEN(0, 255, 0),
    BLUE(0, 0, 255);

    fun rgb() = (r * 256 + g) * 256 + b
}

/**
 * when 使用when代替switch
 * 使用when可以简写很多switch的代码,并且when可以代替if
 */
fun getColorString(color: Color) = when (color) {
//    Color.RED -> "红色"
//    Color.ORANGE -> "橙色"
//    Color.YELLOW -> "黄色"
//    Color.GREEN -> "绿色"
//    Color.BLUE -> "蓝色"
    //when很灵活,可以再多个分支中选择同一个返回,类似switch中去掉break
    Color.RED, Color.YELLOW, Color.ORANGE -> "暖色调"
    Color.GREEN -> "中性色调"
    Color.BLUE -> "冷色调"
}

//val b = 2
//var max = when {
//    a > b -> {
//        println(a)
//        a
//    }
//    else -> b
//}
//
//var max1 = if (a > b) {
//    println(a)
//    a
//} else b

/**
 * 在when中使用任意对象,可以使用集合来作为判断对象
 * setOf 代表一个集合,无序的
 */
fun mix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
    setOf(Color.RED, Color.YELLOW) -> "橙色"
    setOf(Color.YELLOW, Color.BLUE) -> "绿色"
    else -> "我也不知道什么颜色"
}

/**
 * 在when中,甚至可以不写条件,直接判断
 */
fun mix2(c1: Color, c2: Color) = when {
    (c1 == Color.RED && c2 == Color.YELLOW) || (c1 == Color.YELLOW && c2 == Color.RED) -> "橙色"
    (c1 == Color.YELLOW && c2 == Color.BLUE) || (c1 == Color.BLUE && c2 == Color.YELLOW) -> "绿色"
    else -> "不知道什么颜色"
}

/**
 * 智能转换
 * is 相当于Java中的instanceof
 */

//val d = Animal()
//fun anim() {
////    if (d is Dog) {
////        d.dig()
////    }
//    dog.dig()
//}

/**
 * 强转 as
 */
//val d1 = Animal()
//val dog = d1 as Dog


/**
 * 循环
 * while循环和Java中没有任何区别,可以直接用
 * 区间和数列 1..10 1到10,包含10  1 until 10 1到10,不包含10
 */
val oneToTen = 1..10

fun fizzBuzz(i: Int) = when {
    i % 15 == 0 -> "FizzBuzz"
    i % 5 == 0 -> "Buzz"
    i % 3 == 0 -> "Fizz"
    else -> "$i"
}

/**
 *  i 是个形参,写什么都可以,按照for循环的习惯,一般写作i
 *  in 在某个区间中,或者list或者是区间
 */
fun play() {
//    for (i in 1..100) {
//        println(fizzBuzz(i))
//    }

    for (i in 100 downTo 0 step 50) {
        println(fizzBuzz(i))
    }
}

/**
 * 迭代map
 */
fun binary() {
    val binaryReps = TreeMap<Char, String>()//使用TreeMap让键排序
    for (c in 'A'..'F') {//使用字符区间迭代从A到F之间的字符
        val binary = Integer.toBinaryString(c.toInt()) //把A到F的ASCII码转换为二进制
        binaryReps[c] = binary//根据c键把值存入map map[key] = value
//        binaryReps.get(c) = binary//类似list中get(i)
    }
    for ((letter, binary) in binaryReps) {//迭代map时,可以在循环中添加2个参数,一个代表key,一个代表value
        println("$letter = $binary")
    }

    //.withIndex() 相当于给每一个元素多一个index的key element就是他的value
    val list = arrayListOf("10", "11", "1001")
    for ((index, element) in list.withIndex()) {
        println("$index:$element")
    }
}

/**
 * 使用in检查集合和区间的成员
 */
fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'
fun isNumber(c: Char) = c in '0'..'9'

fun regex(c: Char) = when (c) {
    in 'a'..'z', in 'A'..'Z' -> "是个字母"
    in '0'..'9' -> "是个数字"
    else -> "可能是个符号"
}

/**
 * kotlin中的异常
 * 抛出异常
 */
//val c = if (a > 0) a else throw Exception("这是个负数或者0")
//
//var c1: String = "Sting"
//var c2: String? = "123"
//
//fun c22() {
//    c1 = c2!!//一定不为空
//    c2 = c1
//}

/**
 * Int? 代表这个返回值可以为空
 * 在kotlin中?很重要,代表一个类型是否可以为空,如果声明一个类型初始化为空的话,必须加?
 * 在kotlin中,空和非空的类型是两回事儿
 * !! 双叹号代表一定不为空
 */
fun readNumber(reader: BufferedReader): Int? = try {
    //捕获下方代码块中的异常,如果有异常,则凑catch,没有就把try走完
    val line = reader.readLine()
    Integer.parseInt(line)
} catch (e: NumberFormatException) {
    //发生异常后,会走此代码块
    null
} finally {
    //甭管发不发生异常,都会走finally
    reader.close()
}

/**
 * 集合
 * to 并不是一个特殊的结构,是个普通的方法,类似于: 来构建键值对
 */

val set = setOf(1, 2, 3) //相当于new了一个set
val list = listOf(1, 2, 3) //相当于new了一个list集合
val map = mapOf(1 to "one", 2 to "two") //相当于new了一个map

val strings = listOf("first", "second", "fourteenth")
val numbers = setOf(1, 14, 2)

/**
 * kotlin能让方法更好用
 * 命名参数
 * 如果使用了一个命名参数,则其他的参数都必须使用命名参数
 *
 * 默认参数值
 * 默认参数,如果在方法中给了默认参数,则在调用的时候可以不给
 * 如果在有默认参数的情况下,再给了新的参数,则按照新的参数赋值
 */
fun <T> Collection<T>.joinToString(
//    collection: Collection<T>,//集合的顶层接口,代表这个参数可以是list,set
    separator: String = ".",//分隔符
    prefix: String = "{",//开始符号
    postfix: String = "}"//结束符号
): String {
    val result = StringBuilder(prefix) //首先创建一个字符串,默认添加开始符号
    for ((index, element) in this.withIndex()) { //把传进来的list或者set进行循环,给每个item赋值并且添加分隔符
        if (index > 0) {//当item的index=0的时候,也就是第一个元素之前不需要添加分隔符,从index=1开始添加
            result.append(separator)//添加分隔符
        }
        result.append(element)//添加元素
    }
    result.append(postfix)//添加结束符号
    return result.toString()
}

//顶层属性,常量
const val TAG = "TagA"

/**
 * 扩展函数(方法)
 * 给一个已有的类或者类型添加一个方法,在Java中基本不可能办到,String无法继承
 */
fun String.lastChar(): Char = this[this.length - 1]//String的本质是个char的数组,这里的[]表示get出其中的某一个字符,length-1,代表最后一个字符

/**
 * 扩展属性
 * 与方法不同,属性相当于扩展了API,可以用来访问属性,使用的是属性的语法,而不是方法的语法,
 * 被称为属性,但是可以没有任何状态,因为没有地方存储
 */
val String.lastChar: Char
    get() = get(length - 1)


fun main(args: Array<String>) {
//    println("Hello,world")
//    println(max(1, 2))
//    a = 0
//    list.add("kotlin")
//    println(list)
//    println("1+2=${1 + 2}!")
//    println("a ${if (a >= 10) "大于等于10" else "小于10"}")
//    val personJava = PersonJava("LC")
//    personJava.name
//    personJava.age = 12//赋值相当于setAge
//    personJava.age//取值相当于getAge
//
//    val person = Person("LC", 13)
//    person.name
//    person.age = 12

//    val rectangleJava = RectangleJava(10, 10)
//    println(rectangleJava.isSquare)
//
//    val rectangle = Rectangle(10, 12)
//    rectangle.height
//    println(rectangle.isSquare)
//    println(Color.GREEN.rgb())
//    println(getColorString(Color.RED))
//    println(mix(Color.YELLOW, Color.RED))
//    anim()
//    play()
//    binary()
//    println(regex('!'))
//    println(set.javaClass)
//    println(list.javaClass)
//    println(map.javaClass)
//    println(strings.last())
//    println(numbers.max())
//    println(list)
//    println(joinToString(list, "|", "$", "%"))
    //命名参数
    //如果使用了一个命名参数,则其他的参数都必须使用命名参数
//    joinToString(separator = ";", collection = set, prefix = "(", postfix = ")")
    //默认参数,如果在方法中给了默认参数,则在调用的时候可以不给
    //如果在有默认参数的情况下,再给了新的参数,则按照新的参数赋值
//    println(joinToString(list, "|"))
//    println("asd".lastChar())
//    println("lkadflskdfl".lastChar())
//    println(list.joinToString())
    println("as".lastChar)
    println("asd".lastChar())
}

